/*================================================================
 *   Copyright (C) 2021 All rights reserved.
 *   
 *   文件名称：fiber.cc
 *   创 建 者：汉森伯逸
 *   创建日期：2021/3/18
 *   描    述：
 *
 *================================================================*/

#include "fiber.h"
#include "config.h"
#include "log.h"
#include "scheduler.h"


namespace hsby {

// 协程id序号
static std::atomic<uint64_t> g_fiber_id{0};

// 协程总数
static std::atomic<uint64_t> g_fiber_count{0};

// 当前运行的协程
static thread_local Fiber::Ptr t_current_fiber = nullptr;

// 配置协程栈大小
static ConfigVar<uint32_t>::Ptr g_fiber_stack_size =
        Config::Lookup<uint32_t>("fiber.stack_size", 1024*1024, "fiber stack size") ;

class StackAllocactor {
public:
    static void * Alloc(size_t size) {
        return malloc(size);
    }

    static void Destrory(void* p, size_t size) {
        free(p);
    }

};

/**
 * @brief 默认构造函数
 * @description 私有, 用于将当前线程栈作为主协程
 */
Fiber::Fiber() :
        id_(g_fiber_id++),
        thread_id_(GetThisThreadId()){
    state_ = EXEC;

    if (getcontext(&context_)) {
        HSBY_ASSERT_MSG(false, "getcontext error");
    }

    ++g_fiber_count;
}

/**
 * @brief Fiber构造函数
 */
Fiber::Fiber(std::function<void ()> cb, size_t stack_size)
     : id_(g_fiber_id++)
     , thread_id_(GetThisThreadId())
     , stack_size_(stack_size ? stack_size : g_fiber_stack_size->GetValue()){
    // 申请栈内存
    stack_ = StackAllocactor::Alloc(stack_size_);
    HSBY_ASSERT_MSG(stack_, "StackAllocactor::Alloc error");
    Reset(cb);
    ++g_fiber_count;
}

/**
 * @brief Fiber析构函数
 */
Fiber::~Fiber() {
    if (stack_) {
        HSBY_ASSERT(state_ == TERM || state_ == EXCEPT || state_ == INIT);
        StackAllocactor::Destrory(stack_, stack_size_);
        stack_ = nullptr;
    }else {
        HSBY_ASSERT_MSG(!cb_, "cb_ is not NULL in Fiber::~Fiber()");
        HSBY_ASSERT_MSG(state_ == EXEC, "state_ != EXEC in Fiber::~Fiber()");

        if (t_current_fiber.get() == this) {
            SetCurrentFiber(nullptr);
        }
    }
    --g_fiber_count;
}

/**
 * @brief 重置协程正文，并重置状态
 */
void Fiber::Reset(std::function<void()> cb) {
    HSBY_ASSERT(stack_);
    HSBY_ASSERT(state_ == TERM || state_ == EXCEPT || state_ == INIT);
    cb_ = cb;
    // 取得当前栈信息
    if (getcontext(&context_)) {
        HSBY_ASSERT_MSG(false, "getcontext error");
    }
    // 设置栈信息
    context_.uc_link = nullptr;
    context_.uc_stack.ss_sp = stack_;
    context_.uc_stack.ss_size = stack_size_;
    // 设置正文回调
    makecontext(&context_, &Fiber::OnFiber, 0);
}

void Fiber::Dump(std::ostream &os) const {
    os << "Fiber{" << HSBY_VARS_EXP(id_, state_) << "}";
}

/**
 * @brief 取协程Id
 */
uint64_t Fiber::GetId()  {
    return id_;
}
/**
 * @brief 取得当前协程状态
 */
Fiber::State Fiber::GetState() {
    return state_;
}

/**
 * @brief 设置当前协程状态
 */
void Fiber::SetState(Fiber::State st) {
    state_ = st;
}

/**
 * @brief 切入协程
 * @details 把上下文中的协程安装到当前线程, 把当前执行的协程保存为主协程
 */
void Fiber::SwapIn() {
    auto working_fiber = Scheduler::GetThreadWorkingFiber();
    if (!working_fiber) {
        HSBY_LOG_SYSTEM_WARN << HSBY_FUNC_ERROR_EXP;
        return;
    }
    HSBY_LOG_SYSTEM_INFO << "SwapIn:" << *working_fiber << " -> " << *this;
    SetCurrentFiber(shared_from_this());
    SetState(EXEC);
    if (swapcontext(&working_fiber->context_, &context_)) {
        HSBY_ASSERT_MSG(false, "swapcontext error");
    }
}

/**
 * @brief 切换到后台执行
 * @details 把当前协程替换为主协程, 把当前协程保存到上下文中
 */
void Fiber::SwapOut() {
    auto scheduler_fiber = Scheduler::GetThreadWorkingFiber();
    if (!scheduler_fiber) {
        HSBY_LOG_SYSTEM_WARN << HSBY_FUNC_ERROR_EXP;
        return;
    }
    HSBY_LOG_SYSTEM_INFO << "SwapOut:" << *this << " -> " << *scheduler_fiber;
    SetCurrentFiber(scheduler_fiber);
    if (swapcontext(&context_, &scheduler_fiber->context_)) {
        HSBY_ASSERT_MSG(false, "swapcontext error");
    }
}

/**
 * @brief 设置当前协程
 */
void Fiber::SetCurrentFiber(Fiber::Ptr fiber) {
    t_current_fiber = fiber;
}

/**
 * @brief 返回当前协程
 * @details 如果当前线程无协程，则将当前线程栈设置为主协程
 */
Fiber::Ptr Fiber::GetCurrentFiber() {
    if (t_current_fiber) {
        return t_current_fiber;
    }
    std::string scheduler_name;
    auto cur_scheduler = Scheduler::GetCurrentScheduler();
    if (cur_scheduler) {
        scheduler_name = cur_scheduler->GetName();
    }
    t_current_fiber = Fiber::Ptr(new Fiber());
    return t_current_fiber;
}

/**
 * @brief 取得当前协程ID
 */
uint64_t Fiber::GetCurrentFiberId() {
    if (t_current_fiber) {
        return t_current_fiber->GetId();
    }
    return 0;
}

/**
 * @brief 协程切换到后台，并设置为Ready状态
 */
void Fiber::YieldToReady() {
    Fiber::Ptr cur = GetCurrentFiber();
    cur->state_ = READY;
    cur->SwapOut();
}


/**
 * @brief 协程切换到后台，并设置为Hold状态
 */
void Fiber::YieldToHold() {
    Fiber::Ptr cur = GetCurrentFiber();
    HSBY_ASSERT(cur->state_ == EXEC);
    cur->state_ = HOLD;
    cur->SwapOut();
}

/**
 * @brief 协程总数
 */
uint64_t Fiber::GetFiberCount() {
    return g_fiber_count;
}

/**
 * @brief 协程入口
 */
void Fiber::OnFiber() {
    Fiber::Ptr cur = GetCurrentFiber();
    HSBY_ASSERT(cur);
    try {
        cur->cb_();
        cur->cb_ = nullptr;
        cur->state_ = TERM;
    } catch (std::exception& ex) {
        cur->state_ = EXCEPT;
        HSBY_LOG_SYSTEM_ERROR << "Fiber Except: " << ex.what()
                                  << " fiber_id=" << cur->GetId()
                                  << std::endl
                                  << hsby::BackTraceString();
    } catch (...) {
        cur->state_ = EXCEPT;
        HSBY_LOG_SYSTEM_ERROR << "Fiber Except"
                                  << " fiber_id=" << cur->GetId()
                                  << std::endl
                                  << hsby::BackTraceString();
    }

    // 拿出裸指针，释放线程栈中cur对t_fiber的引用
    auto raw_ptr = cur.get();
    HSBY_ASSERT(raw_ptr);
    cur.reset();
    // 切回调度器协程
    raw_ptr->SwapOut();

    // 确保返回
    HSBY_ASSERT_MSG(false, "never reach fiber_id=" + std::to_string(raw_ptr->GetId()));
}


} // namespace hsby